Add `[target.'cfg(...)']` syntax for rustc(doc)flags in .cargo/config
authortee-too <tee-too@users.noreply.github.com>
Mon, 3 Apr 2017 18:35:23 +0000 (20:35 +0200)
committertee-too <tee-too@users.noreply.github.com>
Tue, 4 Apr 2017 15:02:41 +0000 (17:02 +0200)
Allow to use the Rust `cfg(...)` syntax to configure rust(doc)flags.
The flags are concatenated when a build matches several `cfg`, or
several `cfg` and a $triple.

Fix #3499.

src/cargo/ops/cargo_rustc/context.rs
src/doc/config.md
tests/rustflags.rs

index b5d01cd3efde31c8145dd87651221b7ace2ba067..46904749f2598ded992583c5b3274b0f5cc360cb 100644 (file)
@@ -11,7 +11,7 @@ use std::sync::Arc;
 use core::{Package, PackageId, PackageSet, Resolve, Target, Profile};
 use core::{TargetKind, Profiles, Dependency, Workspace};
 use core::dependency::Kind as DepKind;
-use util::{self, CargoResult, ChainError, internal, Config, profile, Cfg, human};
+use util::{self, CargoResult, ChainError, internal, Config, profile, Cfg, CfgExpr, human};
 
 use super::TargetConfig;
 use super::custom_build::{BuildState, BuildScripts};
@@ -178,6 +178,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
                               -> CargoResult<()> {
         let rustflags = env_args(self.config,
                                  &self.build_config,
+                                 &self.info(&kind),
                                  kind,
                                  "RUSTFLAGS")?;
         let mut process = self.config.rustc()?.process();
@@ -872,22 +873,30 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
     }
 
     pub fn rustflags_args(&self, unit: &Unit) -> CargoResult<Vec<String>> {
-        env_args(self.config, &self.build_config, unit.kind, "RUSTFLAGS")
+        env_args(self.config, &self.build_config, self.info(&unit.kind), unit.kind, "RUSTFLAGS")
     }
 
     pub fn rustdocflags_args(&self, unit: &Unit) -> CargoResult<Vec<String>> {
-        env_args(self.config, &self.build_config, unit.kind, "RUSTDOCFLAGS")
+        env_args(self.config, &self.build_config, self.info(&unit.kind), unit.kind, "RUSTDOCFLAGS")
     }
 
     pub fn show_warnings(&self, pkg: &PackageId) -> bool {
         pkg.source_id().is_path() || self.config.extra_verbose()
     }
+
+    fn info(&self, kind: &Kind) -> &TargetInfo {
+        match *kind {
+            Kind::Host => &self.host_info,
+            Kind::Target => &self.target_info,
+        }
+    }
 }
 
 // Acquire extra flags to pass to the compiler from the
 // RUSTFLAGS environment variable and similar config values
 fn env_args(config: &Config,
             build_config: &BuildConfig,
+            target_info: &TargetInfo,
             kind: Kind,
             name: &str) -> CargoResult<Vec<String>> {
     // We *want* to apply RUSTFLAGS only to builds for the
@@ -928,13 +937,34 @@ fn env_args(config: &Config,
         return Ok(args.collect());
     }
 
+    let mut rustflags = Vec::new();
+
     let name = name.chars().flat_map(|c| c.to_lowercase()).collect::<String>();
-    // Then the target.*.rustflags value
+    // Then the target.*.rustflags value... 
     let target = build_config.requested_target.as_ref().unwrap_or(&build_config.host_triple);
     let key = format!("target.{}.{}", target, name);
     if let Some(args) = config.get_list_or_split_string(&key)? {
         let args = args.val.into_iter();
-        return Ok(args.collect());
+        rustflags.extend(args);
+    }
+    // ...including target.'cfg(...)'.rustflags
+    if let Some(ref target_cfg) = target_info.cfg {
+        if let Some(table) = config.get_table("target")? {
+            let cfgs = table.val.iter().map(|(t, _)| (CfgExpr::from_str(t), t))
+                .filter_map(|(c, n)| c.map(|c| (c, n)).ok())
+                .filter(|&(ref c, _)| c.matches(target_cfg));
+            for (_, n) in cfgs {
+                let key = format!("target.'{}'.{}", n, name);
+                if let Some(args) = config.get_list_or_split_string(&key)? {
+                    let args = args.val.into_iter();
+                    rustflags.extend(args);
+                }
+            }
+        }
+    }
+
+    if !rustflags.is_empty() {
+        return Ok(rustflags);
     }
 
     // Then the build.rustflags value
index c67463ffb8b70c2a1bc739235da5a71767edf8ae..1b3ae50c6c2ab4440d406b3edeb8ef84c5faf3cd 100644 (file)
@@ -58,7 +58,8 @@ vcs = "none"
 
 # For the following sections, $triple refers to any valid target triple, not the
 # literal string "$triple", and it will apply whenever that target triple is
-# being compiled to.
+# being compiled to. 'cfg(...)' refers to the Rust-like `#[cfg]` syntax for 
+# conditional compilation.
 [target]
 # For Cargo builds which do not mention --target, this is the linker
 # which is passed to rustc (via `-C linker=`). By default this flag is not
@@ -73,6 +74,13 @@ linker = ".."
 # this value overrides build.rustflags when both are present
 rustflags = ["..", ".."]
 
+[target.'cfg(...)']
+# Similar for the $triple configuration, but using the `cfg` syntax.
+# If several `cfg` and $triple targets are candidates, then the rustflags
+# are concatenated. The `cfg` syntax only applies to rustflags, and not to
+# linker.
+rustflags = ["..", ".."]
+
 # Configuration keys related to the registry
 [registry]
 index = "..."   # URL of the registry index (defaults to the central repository)
index a03f752901a8fe0150901f2997abe6039997c8d3..8cdd31f1ba2fb7b3efa66f647af6e53533b3c6dc 100644 (file)
@@ -950,6 +950,79 @@ fn target_rustflags_precedence() {
                 execs().with_status(101));
 }
 
+#[test]
+fn cfg_rustflags_normal_source() {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "foo"
+            version = "0.0.1"
+        "#)
+        .file("src/lib.rs", "")
+        .file("src/bin/a.rs", "fn main() {}")
+        .file("examples/b.rs", "fn main() {}")
+        .file("tests/c.rs", "#[test] fn f() { }")
+        .file("benches/d.rs", r#"
+            #![feature(test)]
+            extern crate test;
+            #[bench] fn run1(_ben: &mut test::Bencher) { }"#)
+        .file(".cargo/config", "
+            [target.'cfg(feature=\"feat\")']
+            rustflags = [\"-Z\", \"bogus\"]
+            ");
+    p.build();
+
+    assert_that(p.cargo("build").arg("--features").arg("\"feat\"")
+                .arg("--lib"),
+                execs().with_status(101));
+    assert_that(p.cargo("build").arg("--features").arg("\"feat\"")
+                .arg("--bin=a"),
+                execs().with_status(101));
+    assert_that(p.cargo("build").arg("--features").arg("\"feat\"")
+                .arg("--example=b"),
+                execs().with_status(101));
+    assert_that(p.cargo("test").arg("--features").arg("\"feat\""),
+                execs().with_status(101));
+    assert_that(p.cargo("bench").arg("--features").arg("\"feat\""),
+                execs().with_status(101));
+}
+
+// target.'cfg(...)'.rustflags takes precedence over build.rustflags
+#[test]
+fn cfg_rustflags_precedence() {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "foo"
+            version = "0.0.1"
+        "#)
+        .file("src/lib.rs", "")
+        .file(".cargo/config", "
+            [build]
+            rustflags = [\"--cfg\", \"foo\"]
+
+            [target.'cfg(feature = \"feat\"')]
+            rustflags = [\"-Z\", \"bogus\"]
+            ");
+    p.build();
+
+    assert_that(p.cargo("build").arg("--features").arg("\"feat\"")
+                .arg("--lib"),
+                execs().with_status(101));
+    assert_that(p.cargo("build").arg("--features").arg("\"feat\"")
+                .arg("--bin=a"),
+                execs().with_status(101));
+    assert_that(p.cargo("build").arg("--features").arg("\"feat\"")
+                .arg("--example=b"),
+                execs().with_status(101));
+    assert_that(p.cargo("test").arg("--features").arg("\"feat\""),
+                execs().with_status(101));
+    assert_that(p.cargo("bench").arg("--features").arg("\"feat\""),
+                execs().with_status(101));
+}
+
+
+
 #[test]
 fn target_rustflags_string_and_array_form1() {
     let p1 = project("foo")